/*
   几何算法库
   包含：扫描线算法, 射线算法判断点是否在多边形内， 路径压缩算法
*/

#ifndef STUDIO_GEO_ALGO_H
#define STUDIO_GEO_ALGO_H

#include "studio_macros.h"
#include "studio_geo.h"

struct raster_point {
    raster_point() = default;

    raster_point(int _x, int _y) {
        x = _x;
        y = _y;
    }

    int x{0}, y{0};
};

struct cover_pair {
    int beg;
    int end;
};

/// 射线算法判断点是否在多边形内
class ray_algo {
    /// <summary>
    /// 点是否与矢量面相交(射线算法),点的相交在面内或者在边界上都算是相交
    /// </summary>
    /// <param name="mpoly"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    static bool intersect(const studio_poly &mpoly, const studio_point &point);

    static bool intersect(const studio_point &point, const std::vector<studio_point> &points);
};

/// 扫描线算法
class scan_line_algo {
public:

    void set(const studio_geo_rect &rect, const double &cell_size);

    int width() const;

    int height() const;

    double cell_size() const;

    /// <summary>
    /// 扫描单点
    /// </summary>
    /// <param name="line"></param>
    /// <returns></returns>
    bool scanning(const studio_point &point);

    /// <summary>
    /// 扫描多点
    /// </summary>
    /// <param name="points">多点</param>
    /// <returns></returns>
    bool scanning(const studio_multi_point &points);

    /// <summary>
    /// 扫描单线的算法
    /// </summary>
    /// <param name="line"></param>
    /// <returns></returns>
    bool scanning(const studio_line &line);

    /// <summary>
    /// 扫描多条线
    /// </summary>
    /// <param name="lines">多线</param>
    /// <returns></returns>
    bool scanning(const studio_multi_line &lines);

    bool scanning(const std::vector<std::vector<raster_point> > vertices_arr);

    /// <summary>
    /// 扫描一个单点矢量
    /// </summary>
    /// <param name="poly"></param>
    /// <returns></returns>
    bool scanning(const studio_poly &poly);

    /// <summary>
    /// 扫描一个多面矢量
    /// </summary>
    /// <param name="m_polys"></param>
    /// <returns></returns>
    bool scanning(const studio_multi_poly &m_polys);

    void check_line_point(studio_point point, std::vector<raster_point> &vct, int &last_x, int &last_y);

    /// <summary>
    /// 点 线 不会闭合, 可能会存在一些点重合
    /// </summary>
    /// <returns></returns>
    void slim();

    // 测试使用
    void generate_grid_mid_points(studio_multi_point &points)
    {
        // 遍历每行的 row_pairs
        for (const auto &row : m_row_pairs)
        {
            // 获取行号
            int row_idx = row.first;

            // 遍历每一对列范围
            for (const auto &pair : row.second)
            {
                int col_start = pair.beg;  // 起始列
                int col_end = pair.end;    // 结束列

                // 对于该区间内的每一个栅格，计算栅格的中心点坐标
                for (int col = col_start; col <= col_end; ++col)
                {
                    // 计算栅格中心点的 X 和 Y 坐标
                    double x_center = m_rect.min.x + col * m_cell_size + m_cell_size / 2;
                    double y_center = m_rect.max.y - row_idx * m_cell_size + m_cell_size / 2;

                    // 创建新的 studio_point 对象并添加到 points 中
                    points.push_back(studio_point(x_center, y_center));
                }
            }
        }
    }

private:
    // 数据记录以经纬度左上角为原点, 向东为col的正方向,向下为row的正方向
    int m_width{0};
    int m_height{0};
    studio_geo_rect m_rect;
    // 经纬度小数点后6位能精确到1米,更加精确意义不大
    double m_cell_size{0.000001};
    // 记录每一行在矢量内的多对起始列号
    std::map<int, std::vector<cover_pair> > m_row_pairs;
};

/// <summary>
///  矢量(线)抽稀算法
/// </summary>
class geo_vacuate {
public:
    /// <summary>
    /// 对线类型矢量进行抽稀拟合
    /// Douglas-Peucker算法实现
    /// </summary>
    /// <param name="line">矢量线段</param>
    /// <param name="max_points">保留最多点的个数</param>
    /// <param name="epsilon">抽稀容差,若某点到当前线段的最大垂直距离超过epsilon，则保留该点,单位与适量坐标系的单位一致</param>
    /// <param name="vacuate_line">抽稀后的线段</param>
    /// <returns></returns>
    static bool vacuate(const studio_line &line, const int &max_points, const double &epsilon,
                        studio_line &vacuate_line);

    /// <summary>
    /// Douglas-Peucker算法
    /// </summary>
    /// <param name="points"></param>
    /// <param name="start"></param>
    /// <param name="end"></param>
    /// <param name="epsilon">若某点到当前线段的最大垂直距离超过epsilon，则保留该点</param>
    /// <param name="result_indices"></param>
    static void douglas_peucker(const std::vector<studio_point> &points, size_t start, size_t end, double epsilon,
                                std::vector<size_t> &result_indices);


    /// <summary>
    /// 计算点到线段的距离的 平方 （笛卡尔坐标系）
    /// </summary>
    /// <param name="p">点</param>
    /// <param name="start">线段起点</param>
    /// <param name="end">线段终点</param>
    /// <returns></returns>
    static double point_line_dist_square(const studio_point &p, const studio_point &start, const studio_point &end);
};

#endif  // STUDIO_GEO_ALGO_H
